/*
 * Copyright 2010 JAPIS
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.googlecode.xdoclet.ignore;


import java.io.PrintStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.generama.ConfigurableDocletTagFactory;
import org.generama.Plugin;
import org.generama.QDoxCapableMetadataProvider;
import org.generama.VelocityTemplateEngine;
import org.generama.WriterMapper;

import com.thoughtworks.qdox.model.DocletTag;


/**
 * XDoclet2 plugin that ignores unknown tags. Avoids irritating warnings by
 * XDoclet2. This must be the last plugin to run. This implementation depends on
 * {@link ConfigurableDocletTagFactory} breach on
 * {@link ConfigurableDocletTagFactory#getUnknownTags()} list.
 * <p>
 * It is possible to print a <strong>set</strong> of unknown tags by using the
 * {@link #setPrint(boolean)} atribute.
 * </p>
 * 
 * @author japis.3des
 */
public class IgnorePlugin extends Plugin {

    /**
     * The factory keeps unknown tags detected by previous plugins.
     */
    private ConfigurableDocletTagFactory tagFactory;

    /**
     * Plugin message output. Defaults to <code>System.out</code>.
     */
    private PrintStream output = System.out;

    /**
     * See {@link #setPrint(boolean)}.
     */
    private boolean print = true;

    /**
     * Instantiates the plugin.
     * 
     * @param engine velocity engine not used
     * @param provider metadata provider that contains tag factory
     * @param mapper writer mapper not used
     */
    public IgnorePlugin(
            VelocityTemplateEngine engine,
            QDoxCapableMetadataProvider provider,
            WriterMapper mapper) {
        super(engine, provider, mapper);
        this.setDestdir("${project.build.directory}"
                + "/generated-resources"
                + "/xdoclet-ignore");
        this.setValidate(false);
        this.tagFactory = provider.getDocletTagFactory();
    }

    /**
     * Sets the output to be used to print messages.
     * 
     * @param out the print stream to print messages
     */
    void setOutput(PrintStream out) {
        this.output = out;
    }

    /**
     * Indicates that a set of the unknown tags should be displayed. Still, much
     * less verbose than printing all ocurrences. Default: <code>true</code>.
     * 
     * @param b <code>true</code> to print unknown tags, <code>false</code> to
     *            completely ignore them
     */
    public void setPrint(boolean b) {
        this.print = b;
    }

    /**
     * Ignores all unknown tags found by previous plugins. Prints plugin name so
     * user can notice it is working.
     */
    public void start() {
        this.output.println("Running " + this.getClass().getName());
        List unknownTags = this.tagFactory.getUnknownTags();
        if (!unknownTags.isEmpty() && this.print) {
            this.printSet(unknownTags);
        }
        // taking advantage of unprotected list
        unknownTags.clear();
    }

    /**
     * Print a set of tags.
     * 
     * @param tags list of tags
     */
    private void printSet(List tags) {
        SortedSet nameSet = this.readNameSet(tags);
        this.output.println("Unknown tags:");
        for (Iterator it = nameSet.iterator(); it.hasNext();) {
            String name = (String) it.next();
            this.output.println("    \"" + name + "\"");
        }
    }

    /**
     * Return the names of the given tags as a sorted set.
     * 
     * @param tags list of tags
     * @return names in case insensitive sorted set
     */
    private SortedSet readNameSet(List tags) {
        SortedSet set = new TreeSet(String.CASE_INSENSITIVE_ORDER);
        for (Iterator it = tags.iterator(); it.hasNext();) {
            Object object = it.next();
            if (!(object instanceof DocletTag)) {
                continue;
            }
            DocletTag tag = (DocletTag) object;
            String name = tag.getName();
            if (name == null) {
                name = "<null tag name>";
            }
            set.add(name);
        }
        return set;
    }

    /**
     * Required implementation that returns <code>null</code>.
     * 
     * @return <code>null</code>
     */
    protected Collection getMetadata() {
        return null;
    }

}
